package com.yjz.log.print.file;

import android.text.TextUtils;

import androidx.annotation.NonNull;

import com.yjz.log.print.ILogPrinter;
import com.yjz.log.print.view.LogModel;

import java.io.BufferedWriter;
import java.io.File;
import java.io.FileWriter;
import java.io.IOException;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Locale;
import java.util.TimeZone;
import java.util.concurrent.BlockingQueue;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.LinkedBlockingQueue;

/**
 * 作用 : 将log打印打文件
 * 作者 : yjz
 * 时间 : 2024/9/14 16:48
 */
public class FilePrinter implements ILogPrinter {
    private static FilePrinter instance;
    private static final ExecutorService EXECUTOR = Executors.newSingleThreadExecutor();
    // 过期时间
    private final long retentionTime;
    // log文件的路径("/data/data/com.yjz.study/cache/YYYY-MM-DD.txt)
    private final String logFilePath;
    private final LogWriter writer;
    private final PrintWorker worker;

    public FilePrinter(String logFilePath, long retentionTime) {
        this.logFilePath = logFilePath;
        this.retentionTime = retentionTime;
        this.writer = new LogWriter();
        this.worker = new PrintWorker();
        cleanExpiredLog();
    }

    /**
     * 清除过期log
     */
    private void cleanExpiredLog() {
        if (retentionTime <= 0) {
            return;
        }
        long currentTimeMillis = System.currentTimeMillis();
        File logDir = new File(logFilePath);
        File[] files = logDir.listFiles();
        if (files == null) {
            return;
        }
        for (File file : files) {
            if (currentTimeMillis - file.lastModified() > retentionTime) {
                boolean delete = file.delete();
            }
        }
    }

    /**
     * 创建 FilePrinter
     *
     * @param logFilePath   log文件的保存路径，如果是外部路径需要确保已经有外部存储的读写权限
     * @param retentionTime log文件的有效时长，单位毫秒，<=0表示一直有效
     */
    public static synchronized FilePrinter getInstance(String logFilePath, long retentionTime) {
        if (instance == null) {
            instance = new FilePrinter(logFilePath, retentionTime);
        }
        return instance;
    }

    @Override
    public void print(int level, String tag, @NonNull String printStr) {
        long timeMillis = System.currentTimeMillis();
        if (!worker.isRunning()) {
            worker.start();
        }
        worker.put(new LogModel(timeMillis, level, tag, printStr));
    }

    private class PrintWorker implements Runnable {

        private final BlockingQueue<LogModel> logQueue = new LinkedBlockingQueue<>();

        private volatile boolean isRunning;

        public boolean isRunning() {
            synchronized (this) {
                return isRunning;
            }
        }

        public void start() {
            synchronized (this) {
                EXECUTOR.execute(this);
                isRunning = true;
            }
        }

        public void put(LogModel logModel) {
            try {
                logQueue.put(logModel);
            } catch (InterruptedException e) {
                e.getSuppressed();
            }
        }

        @Override
        public void run() {
            try {
                while (isRunning) {
                    if (!logQueue.isEmpty()) {
                        LogModel log = logQueue.take();
                        doPrint(log);
                    }
                }
            } catch (InterruptedException e) {
                e.getSuppressed();
            }
        }

        void doPrint(LogModel log) {
            String lastFileName = writer.getPreFileName();
            if (TextUtils.isEmpty(lastFileName)) {
                String newFileName = genFileName();
                if (writer.isReady()) {
                    boolean close = writer.close();

                }
                if (!writer.ready(newFileName)) {
                    return;
                }
            }
            writer.append(log.flattenedLog());
        }

        String genFileName() {
            SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd", Locale.CHINA);
            sdf.setTimeZone(TimeZone.getDefault());
            return sdf.format(new Date(System.currentTimeMillis()));
        }
    }

    private class LogWriter {
        private String preFileName;
        private File logFile;

        private BufferedWriter bufferedWriter;

        public String getPreFileName() {
            return preFileName;
        }

        public boolean isReady() {
            return bufferedWriter != null;
        }

        /**
         * log写入前的准备操作
         *
         * @param newFileName 要保存log的文件名
         * @return true 表示准备就绪
         */
        public boolean ready(String newFileName) {
            preFileName = newFileName;
            logFile = new File(logFilePath, newFileName + ".txt");
            // 当log文件不存在时创建log文件
            if (!logFile.exists()) {
                try {
                    File parent = logFile.getParentFile();
                    if (parent != null && !parent.exists()) {
                        boolean mkdirs = parent.mkdirs();
                        if (!mkdirs) {
                            return false;
                        }
                    }
                    boolean newFile = logFile.createNewFile();
                    if (!newFile) {
                        return false;
                    }
                } catch (IOException e) {
                    preFileName = null;
                    logFile = null;
                    return false;
                }
            }
            // 创建 bufferedWriter
            try {
                bufferedWriter = new BufferedWriter(new FileWriter(logFile, true));
            } catch (Exception e) {
                preFileName = null;
                logFile = null;
                return false;
            }
            return true;
        }

        /**
         * 关闭bufferedWriter
         */
        public boolean close() {
            if (bufferedWriter != null) {
                try {
                    bufferedWriter.close();
                } catch (IOException e) {
                    return false;
                } finally {
                    bufferedWriter = null;
                    preFileName = null;
                    logFile = null;
                }
            }
            return true;
        }

        /**
         * 将log写入文件
         *
         * @param flattenedLog 格式化后的log
         */
        public void append(String flattenedLog) {
            try {
                bufferedWriter.write(flattenedLog);
                bufferedWriter.newLine();
                bufferedWriter.flush();
            } catch (IOException e) {
                e.getSuppressed();
            }
        }
    }
}
